// SERVER TCP PROGRAM
// revised and tidied up by
// J.W. Atwood
// 1999 June 30
// There is still some leftover trash in this code.

/*
 * Modified for COMP 445 A1
 * Student: Jacqueline Fu and Kevin Labrie
 * Last modify date: Sep. 22th 2012 at 5:19pm
 * Professors: Dr. Ebrahim Soujeri & Dr. Aiman Hanna
 */

/* send and receive codes between client and server */
/* This is your basic WINSOCK shell */
#pragma once
#pragma comment( linker, "/defaultlib:ws2_32.lib" )
#include <winsock2.h>
#include <ws2tcpip.h>
#include <process.h>
#include <winsock.h>
#include <stdio.h>
#include <iostream>
#include <windows.h>
//==================
#include <fstream>
#include<string>
//==================

using namespace std;

//port data types

#define REQUEST_PORT 0x7070

int port=REQUEST_PORT;

#define TRACE 1

//socket data types
SOCKET s;

SOCKET s1;
SOCKADDR_IN sa;      // filled by bind
SOCKADDR_IN sa1;     // fill with server info, IP, port
union {struct sockaddr generic;
	struct sockaddr_in ca_in;}ca;

int calen=sizeof(ca); 

int const MAX_SIZE = 128;
int const MAX = 260;
//buffer data types
char szbuffer[128];
char *buffer;
int ibufferlen;
int ibytesrecv;
int ibytessent;

//host data types
char localhost[128];
	HOSTENT *hp;

//wait variables
int nsa1;
int r,infds=1, outfds=0;
struct timeval timeout;
const struct timeval *tp=&timeout;
	fd_set readfds;

//others
HANDLE test;
DWORD dwtest;

//other variables
int bitsread;
int bitsleft;

//reference for used structures

/*  * Host structure

struct  hostent 
{
    char    FAR * h_name;             official name of host *
    char    FAR * FAR * h_aliases;    alias list *
    short   h_addrtype;               host address type *
    short   h_length;                 length of address *
    char    FAR * FAR * h_addr_list;  list of addresses *
	#define h_addr  h_addr_list[0]    address, for backward compat *
};

 * Socket address structure

struct sockaddr_in 
{
	short   sin_family;
	u_short sin_port;
	struct  in_addr sin_addr;
	char    sin_zero[8];
}; sa_in*/

struct CONTROL_FRAME 
{
	unsigned char direction;	//GET or PUT
	char fname[MAX];			//filename
} control_frame;

struct MESSAGE_FRAME
{
	unsigned char header;		//ERROR,DATA,LASTPACKET, etc.
	char data[MAX_SIZE]; 		//data or error message
} message_frame;

struct DATA_FRAME 
{
	unsigned char header;		//ERROR,DATA,LASTPACKET, etc.
	char data[260]; 		//data or error message
} data_frame;

int main(void)
{
	//check if serverlog exists
	ifstream filelogcheck;
	filelogcheck.open("serverlog.txt");

	if (filelogcheck.is_open()) //file exists
	{
		//close file
		filelogcheck.close();

		//delete old serverlog when server starts
		remove("serverlog.txt");
	}

	//close file
	filelogcheck.close();

	WSADATA wsadata;

	try
	{        		 
		if (WSAStartup(0x0202,&wsadata)!=0)
		{  
			cout<<"Error in starting WSAStartup()\n";
		}
		else
		{
			buffer="WSAStartup was suuccessful\n";   
			WriteFile(test,buffer,sizeof(buffer),&dwtest,NULL); 

			/* display the wsadata structure */
			cout<< endl
				<< "wsadata.wVersion "       << wsadata.wVersion       << endl
				<< "wsadata.wHighVersion "   << wsadata.wHighVersion   << endl
				<< "wsadata.szDescription "  << wsadata.szDescription  << endl
				<< "wsadata.szSystemStatus " << wsadata.szSystemStatus << endl
				<< "wsadata.iMaxSockets "    << wsadata.iMaxSockets    << endl
				<< "wsadata.iMaxUdpDg "      << wsadata.iMaxUdpDg      << endl;
		}  

		//Display info of local host

		gethostname(localhost,10);
		cout << "hostname: " <<localhost << endl;

		if((hp=gethostbyname(localhost)) == NULL) 
		{
			cout << "gethostbyname() cannot get local host info?"
				<< WSAGetLastError() << endl; 
			exit(1);
		}

		//Create the server socket
		if((s = socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET) 
			throw "can't initialize socket";
		// For UDP protocol replace SOCK_STREAM with SOCK_DGRAM 


		//Fill-in Server Port and Address info.
		sa.sin_family = AF_INET;
		sa.sin_port = htons(port);
		sa.sin_addr.s_addr = htonl(INADDR_ANY);


		//Bind the server port
		if (bind(s,(LPSOCKADDR)&sa,sizeof(sa)) == SOCKET_ERROR)
			throw "can't bind the socket";
		cout << "Bind was successful" << endl;

		//Successfull bind, now listen for client requests.
		if(listen(s,10) == SOCKET_ERROR)
			throw "couldn't  set up listen on socket";
		else cout << "Listen was successful" << endl;

		FD_ZERO(&readfds);

		//while loop for server
		while(1)
		{
			//create log file and open for writting
			FILE* filelog;
			filelog = fopen("serverlog.txt", "a");

			FD_SET(s,&readfds);  //always check the listener

			if(!(outfds=select(infds,&readfds,NULL,NULL,tp))) {}

			else if (outfds == SOCKET_ERROR) throw "failure in Select";

			else if (FD_ISSET(s,&readfds))  cout << "got a connection request" << endl; 

			//Found a connection request, try to accept. 

			if((s1=accept(s,&ca.generic,&calen))==INVALID_SOCKET)
				throw "Couldn't accept connection\n";
			else
				//Connection request accepted.
				cout<<"accepted connection from " << inet_ntoa(ca.ca_in.sin_addr) << ":"
					<<hex<<htons(ca.ca_in.sin_port) << endl;

			//variable to hold clenthostname
			char clientHostName[128] = {0};

			//Fill in szbuffer from accepted request.
			if((ibytesrecv = recv(s1,szbuffer,128,0)) == SOCKET_ERROR)
				throw "Receive error in server program\n";
			else
			{
				for(int i = 0; i < strlen(szbuffer); i++)
					clientHostName[i] = szbuffer[i]; 
			}

			//print info to log
			if(TRACE)
			{
				fprintf(filelog, "Server connected to %s\n", clientHostName);
			}

			while(true)
			{
				//notify user making a request
				cout << "Waiting for user " << clientHostName << " to make a request..." << endl;

				//get control_frame from client
				ibytesrecv = recv(s1, (char *)&control_frame, sizeof(control_frame), 0);

				//check for error else display direction of tranfer delete is char d, get (send) is char g and put (store) is char p
				if(ibytesrecv == SOCKET_ERROR)
					throw "Receive error in server program\n";
				else
				{
					if(control_frame.direction == 'q')
						break;
					else
					if(control_frame.direction == 'l')
						cout << clientHostName << " requests to list all files on server." << endl;
					else
					if(control_frame.direction == 'd')
						cout << clientHostName << " requests file " << control_frame.fname << " to be deleted from the server." << endl;
					else
					if(control_frame.direction == 'g')
						cout << clientHostName << " requests file " << control_frame.fname << " to be sent from the server." << endl;
					else
					if(control_frame.direction == 'p')
						cout << clientHostName << " requests file " << control_frame.fname << " to be stored on the server." << endl;
				}

				//direction is list i.e. char l, list all files on server
				if(control_frame.direction == 'l')
				{
					//directory search variables
					WIN32_FIND_DATA fd;
					HANDLE h = FindFirstFile("*.*", &fd);
 
					while(1)
					{
						//if no files on server send error message
						if(h == INVALID_HANDLE_VALUE)
						{
							data_frame.header = 'e'; //error no files on server

							//send error packet
							send(s1, (char *)&data_frame, sizeof(data_frame), 0);
							break; // no files found
						}

						//if there are files on the server
						data_frame.header = 'f'; //char f indicates first or middle file on server

						//clear data_frame.data
						memset(data_frame.data, 0, sizeof(data_frame.data));

						//place file name into packet
						strcpy_s(data_frame.data,sizeof(data_frame.data),fd.cFileName);

						//send file name to client
						send(s1, (char *)&data_frame, sizeof(data_frame), 0);
 
						//if no more files on server left to display
						if(FindNextFile(h, &fd) == FALSE)
						{
							data_frame.header = 'l'; //char l indicates no more files left on server

							//clear data_frame.data
							memset(data_frame.data, 0, sizeof(data_frame.data));

							//send packet indicating no more files left on the server
							send(s1, (char *)&data_frame, sizeof(data_frame), 0);
							break;
						}
					}
				}
				else //direction is delete i.e. char d, delete requested file from server
				if(control_frame.direction == 'd')
				{
					//open file to be deleted as filedel 
					ifstream filedel;

					//open for input in binary mode (mode doesn't really matter)
					filedel.open(control_frame.fname, ios_base::in | ios_base::binary );

					//check if the requested file exists
					if (!filedel.is_open()) //file does not exist
					{
						//send packet to client indicating the file doesn't exist on the server
						message_frame.header = 'e'; //char e indicates an error
					
						//send error message to client
						send(s1, (char *)&message_frame, sizeof(message_frame), 0);

						//if file does not exist: error message
						cout <<"ERROR: File Does Not Exist!" << endl;
						cout << "File " << control_frame.fname << " cannot be deleted!" << endl;
					}
					else //file exists on server
					{
						//close file
						filedel.close();

						//delete requested file
						remove(control_frame.fname);

						//send packet to client indicating the file doesn't exist on the server
						message_frame.header = 's'; //char s indicates success

						//send success message to client
						send(s1, (char *)&message_frame, sizeof(message_frame), 0);
					}
				}
				else //if direction is get i.e. char g, send file to client
					if(control_frame.direction == 'g')
					{
						//message saying file is being sent
						cout << "Sending file to " << clientHostName << "..." << endl;

						//print info to log
						if(TRACE)
						{
							fprintf(filelog, "Sender: sending file %s to %s\n", control_frame.fname, clientHostName);
						}

						//open file to be sent as fileout 
						ifstream fileout;

						//open for input in binary mode
						fileout.open(control_frame.fname, ios_base::in | ios_base::binary );

						//check if the requested file exists
						if (!fileout.is_open()) //file does not exit
						{
							//send packet to client indicating the file doesn't exist on the server
							message_frame.header = 'e'; //char e indicates an error
					
							//send error message to client
							send(s1, (char *)&message_frame, sizeof(message_frame), 0);

							//if file does not exist: error message
							cout <<"ERROR: File Does Not Exist!" << endl;
							cout << "File " << control_frame.fname << " cannot be sent to client " << clientHostName << "!" << endl;
						}
						else //file exists
						{
							//get the file size
							fileout.seekg (0, ios::end);
							int filesize = fileout.tellg();
							fileout.seekg (0, ios::beg);

							//keep track of the bits already read
							bitsread = 0;

							//keep track of the bits left to read
							bitsleft = filesize;

							//bool to flag when the file as been completely received
							bool lastpacket = false;

							//keep sending packets containing the file data until there are no more bits to read
							while(lastpacket == false)
							{
								//if data is at the begining or middle of the file
								if(bitsleft > MAX_SIZE)
								{
									//char b means this packet is in the middle
									message_frame.header = 'b';

									//read data into the packet until the alloted size is reached
									fileout.read(message_frame.data, MAX_SIZE);

									//change bits read and bits left accordingly
									bitsleft = bitsleft - MAX_SIZE;
									bitsread = bitsread + MAX_SIZE;

									//send packet to server
									send(s1, (char *)&message_frame, sizeof(message_frame), 0);

									//print info to log
									if(TRACE)
									{
										fprintf(filelog, "Sender: packet sent to %s: %dbytes\n", clientHostName, MAX_SIZE);
									}

									//flag that this is not the last packet
									lastpacket = false;
								}
								else //if data is at the end of the file
									if(bitsleft <= MAX_SIZE)
									{
										//means this packet is the last
										message_frame.header = (unsigned char)bitsleft;

										//since we will not fill the entire message_frame.data, clear it
										memset(message_frame.data, 0, sizeof(message_frame.data));

										//read data into the packet until the alloted size is reached
										fileout.read(message_frame.data, bitsleft);

										//change bits read and bits left accordingly
										bitsleft = bitsleft - bitsleft;
										bitsread = bitsread + bitsleft;

										//send packet to server
										send(s1, (char *)&message_frame, sizeof(message_frame), 0);

										//print info to log
										if(TRACE)
										{
											fprintf(filelog, "Sender: last packet sent to %s: %dbytes\n", clientHostName, message_frame.header);
										}

										//flag that this is the last packet
										lastpacket = true;
									}
							}
						}

						//get file size
						fileout.seekg(0, ios::end);
						int filesizeout = fileout.tellg();

						//close the file
						fileout.close();

						//print info to log
						if(TRACE)
						{
							fprintf(filelog, "Sender: file %s completely sent to %s: %dbytes\n", control_frame.fname, clientHostName, filesizeout);
						}

						//send message as long as there is no error
						if(message_frame.header != 'e')
						{
							//message saying file has been sent
							cout << "File " << control_frame.fname << " sent to client " << clientHostName << "!" << endl;
						}
					}
					else //direction is put i.e. char p, store file sent from client
					if(control_frame.direction == 'p')
					{
						//message saying file is being received
						cout << "Receiving file from " << clientHostName << "..." << endl;

						//print info to log
						if(TRACE)
						{
							fprintf(filelog, "Reciever: recieving file %s from %s\n", control_frame.fname, clientHostName);
						}

						//create file to write to as filein
						ofstream filein;

						//open for output in binary mode (overwrite file if it already exists)
						filein.open(control_frame.fname, ios::out | ios::binary );

						//bool to flag when the file as been completely received
						bool lastpacket = false;

						//continue recieving packets of file until none are left i.e. receive packet indicating end of file
						while(lastpacket == false)
						{
							//get message_frame from client
							ibytesrecv = recv(s1, (char *)&message_frame, sizeof(message_frame), 0);

							//check for error else recieve packets
							if(ibytesrecv == SOCKET_ERROR)
								throw "Receive error in server program\n";
							else //no error
							{
								switch (message_frame.header)
								{
									case 'b': //beginning or middle of file
										//write packet to file
										filein.write (message_frame.data, sizeof(message_frame.data));

										//print info to log
										if(TRACE)
										{
											fprintf(filelog, "Reciever: recieved a packet from %s: %dbytes\n", clientHostName, sizeof(message_frame.data));
										}

										//flag that this is not the last packet
										lastpacket = false;
										break;
									default: //end of file
										//write packet to file
										filein.write (message_frame.data, (int)message_frame.header);

										//print info to log
										if(TRACE)
										{
											fprintf(filelog, "Reciever: recieved last packet from %s: %dbytes\n", clientHostName, (int)message_frame.header);
										}
	
										//flag that this is the last packet
										lastpacket = true;
										break;
								}
							}
						}

						//get file size
						filein.seekp(0, ios::end);
						int filesizein = filein.tellp();

						//close the file
						filein.close();

						//print info to log
						if(TRACE)
						{
							fprintf(filelog, "Reciever: transfer file %s from %s completed: %dbytes\n", control_frame.fname, clientHostName, filesizein);
						}

						//message saying file has been received
						cout << "File " << control_frame.fname << " received from client " << clientHostName << "!" << endl;
					}

			}//while loop for connected client

			//message that client has disconnected
			cout << clientHostName << " has disconnected." << endl << "Waiting for next connection..." << endl;

			//print info to log
			if(TRACE)
			{
				fprintf(filelog, "Server disconnected from %s\n", clientHostName);
			}

			//close log file
			fclose (filelog);

		}//while loop for server

	} //try loop

	//Display needed error message.

	catch(char* str) { cerr<<str<<WSAGetLastError()<<endl;}

	//close Client socket
	closesocket(s1);		
	
	//close server socket
	closesocket(s);
	
	/* When done uninstall winsock.dll (WSACleanup()) and exit */ 
	WSACleanup();

	//message that program is exiting
	cout << "System is quitting..." << endl;

	return 0;
}